#pragma once
#include <iostream>
#include <string>
#include <atomic>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <boost/algorithm/string.hpp>

namespace NS_Util
{

    const std::string temp_path = "./temp/";

    class PathUtil
    {
    public:
        static std::string AddSuffix(const std::string& file_name, const std::string& suffix)
        {
            std::string path_name = temp_path + file_name + suffix;
            return path_name;
        }
        // 构建源文件的全路径
        static std::string Src(const std::string& file_name)
        {
            return AddSuffix(file_name, ".cpp");
        }
        // 构建可执行程序的全路径
        static std::string Exe(const std::string& file_name)
        {
            return AddSuffix(file_name, ".out");
        }
        // 构建编译错误的全路径
        static std::string CompilerError(const std::string& file_name)
        {
            return AddSuffix(file_name, ".cerr");
        }
        // 构建标准错误的全路径
        static std::string Stderr(const std::string& file_name)
        {
            return AddSuffix(file_name, ".serr");
        }
        // 构建标准输入的全路径
        static std::string Stdin(const std::string& file_name)
        {
            return AddSuffix(file_name, ".sin");
        }
        // 构建标准输出的全路径
        static std::string Stdout(const std::string& file_name)
        {
            return AddSuffix(file_name, ".sout");
        }
    };

    class TimeUtil
    {
    public:
        static std::string GetTimeStamp()
        {
            struct timeval time_val;
            gettimeofday(&time_val, nullptr);
            return std::to_string(time_val.tv_sec);
        }
        // 获得毫秒级的时间戳
        static std::string GetTimeMs()
        {
            struct timeval time_val;
            gettimeofday(&time_val, nullptr);
            return std::to_string(time_val.tv_sec * 1000 + time_val.tv_usec / 1000);
        }
    };

    class FileUtil
    {
    public:
        static bool IsFileExists(const std::string& file_name)
        {
            struct stat st;
            if (stat(file_name.c_str(), &st) == 0) // 获取文件属性，以判断文件是否存在
                return true;
            return false;
        }

        static int OpenFile(const std::string& file_name)
        {
            umask(0);
            int fd = open(file_name.c_str(), O_CREAT | O_WRONLY, 0664);
            return fd;
        }

        // 生成唯一的文件名
        static std::string UniqueFileName()
        {
            static std::atomic_uint id(0);
            ++id;
            std::string ms_time = TimeUtil::GetTimeMs();
            std::string uniq_id = std::to_string(id);
            return uniq_id + "_" + ms_time;
        }

        static bool WriteFile(const std::string& file_name, const std::string& content)
        {
            std::ofstream ofs(file_name);
            if (!ofs.is_open())
                return false;

            ofs.write(content.c_str(), content.size());
            ofs.close();

            return true;
        }

        static bool ReadFile(const std::string& file_name, std::string* content, bool keep = false)
        {
            content->clear();

            std::ifstream ifs(file_name);
            if (!ifs.is_open())
                return false;

            std::string line;
            //getline不保留行分隔符
            while (std::getline(ifs, line))
            {
                *content += (line + (keep ? "\n" : ""));
            }
            ifs.close();

            return true;
        }

        static void DeleteTempFile(const std::string& file_name)
        {
            std::string src = PathUtil::Src(file_name);
            if (IsFileExists(src))
                unlink(src.c_str());
            std::string exe = PathUtil::Exe(file_name);
            if (IsFileExists(exe))
                unlink(exe.c_str());

            std::string cerr = PathUtil::CompilerError(file_name);
            if (IsFileExists(cerr))
                unlink(cerr.c_str());
            std::string sin = PathUtil::Stdin(file_name);
            if (IsFileExists(sin))
                unlink(sin.c_str());
            std::string sout = PathUtil::Stdout(file_name);
            if (IsFileExists(sout))
                unlink(sout.c_str());
            std::string serr = PathUtil::Stderr(file_name);
            if (IsFileExists(serr))
                unlink(serr.c_str());
        }
    };

    class StringUtil
    {
    public:
        static void SpiltString(const std::string& src, std::vector<std::string>* target,
            const std::string& sep)
        {
            boost::split(*target, src, boost::is_any_of(sep), boost::algorithm::token_compress_on);
        }

    };

    class StatusUtil
    {
    public:
        /*************************************
         * code > 0 : 程序收到信号异常退出
         * code = 0 : 全部完成
         * code < 0 : 文件操作失败、代码为空、编译失败等
        */
        static std::string CodeToDesc(int code, const std::string& file_name)
        {
            std::string desc;
            switch (code)
            {
            case -1:
                desc = "the code submitted by the user is empty";
                break;
            case -2:
                desc = "unknown error";
                break;
            case -3:
                FileUtil::ReadFile(PathUtil::CompilerError(file_name), &desc, true);
                break;
            case  0:
                desc = "all success";
                break;
            case SIGABRT:
                desc = "excessive memory use";
                break;
            case SIGXCPU:
                desc = "cpu used timeout";
                break;
            case SIGFPE:
                desc = "float point overflow";
                break;
            case SIGSEGV:
                desc = "segmentation violation";
                break;
            default:
                desc = "unkown status code: " + std::to_string(code);
                break;
            }
            return desc;
        }

    };

}
